package org.infodavid.common.registry.impl;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * The Class MapRegistry.
 * @param <K> the type of the keys
 * @param <E> the type of the elements
 */
public class MapRegistry<K extends Serializable,E extends Serializable> extends
    AbstractRegistry<K,E> {
  /** Logger. */
  private static final Log LOGGER = LogFactory.getLog(MapRegistry.class);

  /**
   * The Class ValueAsKeyGenerator.
   * @param <V> the type of the value
   */
  public static class ValueAsKeyGenerator<V extends Serializable> implements IKeyGenerator<V,V> {

    /**
     * Creates the array.
     * @param <T> the generic type
     * @param items the items
     * @return the t[]
     */
    @SafeVarargs
    public static <T> T[] createArray(final T... items) {
      return items;
    }

    /*
     * (non-Javadoc)
     * @see org.infodavid.common.registry.AbstractRegistry.KeyGenerator#getKey(java.io.Serializable)
     */
    @Override
    public V[] getKeys(final V elt) {
      return createArray(elt);
    }
  }

  /** The elements. */
  private final transient Map<K,E> elements;

  /**
   * Instantiates a new map registry.
   * @param generator the key generator
   */
  public MapRegistry(final IKeyGenerator<K,E> generator) {
    super(generator);

    elements = new HashMap<K,E>();
  }

  /**
   * Instantiates a new default manager impl.
   * @param map the source map
   * @param generator the key generator
   */
  public MapRegistry(final Map<K,E> map, final IKeyGenerator<K,E> generator) {
    super(generator);

    elements = map;
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.registry.impl.AbstractRegistry#doClear()
   */
  @Override
  protected void doClear() {
    getMap().clear();
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.registry.IRegistry#getEntries()
   */
  @Override
  public Collection<E> getEntries() {
    return Collections.unmodifiableCollection(elements.values());
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.registry.impl.AbstractRegistry#doGetEntry(java.io.Serializable)
   */
  @Override
  protected E doGetEntry(final K key) {
    return elements.get(key);
  }

  /**
   * Gets the map.
   * @return the map of elements (key => element)
   */
  public Map<K,E> getMap() {
    return elements;
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.registry.impl.AbstractRegistry#doRegister(java.io.Serializable)
   */
  @Override
  protected E doRegister(final E elt) {
    final K[] keys = getKeyGenerator().getKeys(elt);

    if (keys == null || keys.length == 0) {
      LOGGER.warn("Key generator returns no key for element: " + elt);
    }
    else {
      for (final K key : keys) {
        elements.put(key, elt);
      }
    }

    return elt;
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.registry.impl.AbstractRegistry#doUnregister(java.io.Serializable)
   */
  @Override
  protected E doUnregister(final E elt) {
    final K[] keys = getKeyGenerator().getKeys(elt);

    if (keys == null || keys.length == 0) {
      LOGGER.warn("Key generator returns no key for element: " + elt);
    }
    else {
      for (final K key : keys) {
        elements.remove(key);
      }
    }

    return elt;
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.registry.IRegistry#containsEntry(java.io.Serializable)
   */
  @Override
  public boolean containsEntry(final E entry) {
    return elements.containsValue(entry);
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.registry.IRegistry#containsKey(java.io.Serializable)
   */
  @Override
  public boolean containsKey(final K key) {
    return elements.containsKey(key);
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.registry.IRegistry#size()
   */
  @Override
  public long size() {
    return elements.size();
  }
}
